/*
 *  (c) 2015 fullhan.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <environment.h>
#include <asm/arch/hardware.h>
#include <netdev.h>
#include <asm-generic/gpio.h>
#include "pinctrl.h"
#include "board_config.h"
#include <fh_dma.h>
#include <phy.h>
#include <spi.h>
DECLARE_GLOBAL_DATA_PTR;
extern env_t *env_ptr;

static int realtec_loop_wait_linkup(struct phy_device *p_phy);
static int realtec_loop_spd_set(struct phy_device *p_phy, u32 spd);
struct phy_reg_cfg phy_config_array[] = {
	{
		.id = FH_GMAC_PHY_RTL8201,
		.list = {
			{W_PHY, 0x1f, 0x7},
			{W_PHY, 0x10, 0x1ffa},
			{W_PHY, 0x1f, 0},
			{0},
		},
		.inf_sup = PHY_INTERFACE_MODE_RMII,
	},
	{
		.id = FH_GMAC_PHY_INTERNAL_V2,
		.list = {
			{0},
		},
		.inf_sup = PHY_INTERFACE_MODE_MII,
	},
	{
		.id = FH_GMAC_PHY_RTL8211F,
		.list = {
			{W_PHY, 0x1f, 0x0},
			{W_PHY, 0x0d, 0x7},
			{W_PHY, 0x0e, 0x3c},
			{W_PHY, 0x0d, 0x4007},
			{W_PHY, 0x0e, 0x0},

			{0},
		},
		.inf_sup = PHY_INTERFACE_MODE_RGMII,
		.dll_enable = DLL_SETTING_ENABLE,
		.loop_spd_set = realtec_loop_spd_set,
		.loop_wait_linkup = realtec_loop_wait_linkup,
	},
	{
		.id = FH_GMAC_PHY_JL2101,
		.list = {
			{0},
		},
		.inf_sup = PHY_INTERFACE_MODE_RGMII,
	},
	{
		.id = FH_GMAC_PHY_MAE0621,
		.list = {
			{W_PHY, 0x1f, 0x0d92},
			{W_PHY, 0x1f, 0x0d92},
			{W_PHY, 0x02, 0x200a},
			{W_PHY, 0x1f, 0x0},

			{W_PHY, 0x1f, 0x0},
			{W_PHY, 0x0d, 0x7},
			{W_PHY, 0x0e, 0x3c},
			{W_PHY, 0x0d, 0x4007},
			{W_PHY, 0x0e, 0x0},
			{W_PHY, 0x0, 0x9140},
			{W_PHY, 0x1f, 0x0},

			{0},
		},
		.inf_sup = PHY_INTERFACE_MODE_RGMII,
	},
	{0},
};
int realtec_loop_wait_linkup(struct phy_device *p_phy){
	int reg_ret;
	int timeout = 0xffffff;

	if(!p_phy){
		printf("null phy dev.....check if bind ok..\n");
		return -1;
	}
	do{
		timeout--;
		//fake read..
		reg_ret = p_phy->bus->read(p_phy->bus, 0, 0, 1);
		reg_ret = p_phy->bus->read(p_phy->bus, 0, 0, 1);
	}while(timeout && !(reg_ret & 0x04));

	if(timeout == 0){
		printf("wait linkup timeout...\n");
		return -1;
	}
	return 0;
}
int realtec_loop_spd_set(struct phy_device *p_phy, u32 spd){

	if(!p_phy){
		printf("null phy dev.....check if bind ok..\n");
		return -1;
	}
	p_phy->bus->write(p_phy->bus, 0, 0, 31, 0);
	p_phy->bus->write(p_phy->bus, 0, 0, 0, 0x8000);
	//wait 20ms for phy reset... realtek askfor it
	mdelay(20);
	switch(spd){
		case 1000:
		p_phy->bus->write(p_phy->bus, 0, 0, 0, 0x40 | 1 << 14);
		break;

		case 100:
		p_phy->bus->write(p_phy->bus, 0, 0, 0, 0x2000 | 1 << 14);
		break;

		case 10:
		p_phy->bus->write(p_phy->bus, 0, 0, 0, 0x00 | 1 << 14);
		break;

		default:
		printf("unknow spd %d\n",spd);
		return -2;
	}
	//wait 20ms for link...realtek askfor it
	mdelay(20);
	return 0;
}
void soc_reg_update(struct phy_device *phydev, void *pri){
	unsigned int tmp[2];
	struct soc_reg_tuning *soc_rec_dll = (struct soc_reg_tuning *)pri;

	if(sizeof(struct soc_reg_tuning) > sizeof(tmp)){
		printf("soc_reg_update :: memleak...\n");
		return;
	}
	memcpy(tmp, soc_rec_dll, sizeof(struct soc_reg_tuning));

	SET_REG_M(REG_PMU_WREN, 3 << 14, 3 << 14);
	SET_REG(REG_PMU_GMAC_TUNING_0, tmp[0]);
	SET_REG(REG_PMU_GMAC_TUNING_1, tmp[1]);
	SET_REG_M(REG_PMU_WREN, 0 << 14, 3 << 14);
}


void fh_gmac_phy_reset(void)
{
	#if(1)
	gpio_direction_output(CONFIG_GPIO_EMACPHY_RESET, 1);
	udelay(150000);
	gpio_direction_output(CONFIG_GPIO_EMACPHY_RESET, 0);
	udelay(150000);
	gpio_direction_output(CONFIG_GPIO_EMACPHY_RESET, 1);
	udelay(150000);
	#endif
}

void fh_qos_set_phy_dll_val(u32 tx_val, u32 rx_val){
	SCU_UNLOCK;
	//printf("dll setting :: tx : rx == %x : %x\n",tx_val, rx_val);
	SET_REG_M(REG_PMU_DLL, tx_val << 8 | rx_val, 0xffff);
	SCU_LOCK;
}


void fh_qos_set_phy_polarity(u32 tx_pol, u32 rx_pol){
	SCU_UNLOCK;
	//printf("pol setting :: tx : rx == %x : %x\n",tx_pol, rx_pol);
	SET_REG_M(REG_PMU_CLK_SEL2, tx_pol << 23 | rx_pol << 24, 0x3 << 23);
	SCU_LOCK;
}


static void fh_external_phy_link_up_cb(void *pri)
{

	struct phy_device *p_phy;
	struct phy_reg_cfg *p_reg_cfg;
	int i;

	p_phy = (struct phy_device *)pri;
	if(!p_phy)
			return;
	for(i = 0, p_reg_cfg = phy_config_array;
	i < ARRAY_SIZE(phy_config_array); i++, p_reg_cfg++){
		//printf("p_phy->phy_id : p_reg_cfg->id = %x : %x\n",p_phy->phy_id, p_reg_cfg->id);
		if(p_phy->phy_id == p_reg_cfg->id){
			if(p_reg_cfg->dll_enable == DLL_SETTING_ENABLE){
				//printf("goto set dll~~~~~~~~p_phy->speed is %d\n",p_phy->speed);
				SCU_UNLOCK;
				if(p_phy->speed == 10){
					fh_qos_set_phy_polarity(p_reg_cfg->tx_10_polarity, p_reg_cfg->rx_10_polarity);
					fh_qos_set_phy_dll_val(p_reg_cfg->tx_10_dll_val, p_reg_cfg->rx_10_dll_val);
				}
				else if(p_phy->speed == 100){
					fh_qos_set_phy_polarity(p_reg_cfg->tx_100_polarity, p_reg_cfg->rx_100_polarity);
					fh_qos_set_phy_dll_val(p_reg_cfg->tx_100_dll_val, p_reg_cfg->rx_100_dll_val);
				}
				else if(p_phy->speed == 1000){
					fh_qos_set_phy_polarity(p_reg_cfg->tx_1000_polarity, p_reg_cfg->rx_1000_polarity);
					fh_qos_set_phy_dll_val(p_reg_cfg->tx_1000_dll_val, p_reg_cfg->rx_1000_dll_val);
				}
				SCU_LOCK;
			} else {
				if(p_phy->speed == 1000){
						printf("Warning :: you may need to scan 1000M pll ....\n");
				}
			}
			//break match id..
			break;
		}
	}

}


static void fh_external_phy_link_down_cb(void *pri)
{

}



#ifdef CONFIG_USB
void fh_usb_power_enable(void)
{
	SET_REG_M(REG_PMU_USB_SYS1, 0x0, 0x400);
	gpio_direction_output(CONFIG_GPIO_USB_PWREN, 1);
	
	SET_REG_M(REG_PMU_USB_SYS0, 0x11, 0x11);
	udelay(1000);
	SET_REG_M(REG_PMU_USB_SYS0, 0x0, 0x11);
}
#endif

/* Select SD1 function from SPI0 to SD1 */
void fh_sd1_function_switch(void)
{
	//SET_REG_M(REG_PMU_SD1_FUNC_SEL, 0x3, 0x3);
}

/* Get boot mode */
int get_boot_media(void)
{
	int boot_mode;

	boot_mode = GET_REG(REG_PMU_BOOT_MODE);

	return boot_mode;
}

/* Get which eMMC port to save env */
int mmc_get_env_dev(void)
{
	int boot_mode = 0;
	int dev = 0;

	boot_mode = GET_REG(REG_PMU_BOOT_MODE);
	switch(boot_mode)
	{
		case FH_SD0_EMMC_FLASH:
		default:
			dev = 0;
			break;
		case FH_SD1_EMMC_FLASH:
			dev = 1;
			break;
	}

	return dev;
}

void clock_init(void)
{
	SCU_UNLOCK;

	//timer0
	SET_REG_M(REG_PMU_CLK_GATE0, 0x0, 0x02000000);
#if 0
	//SPI0
	SET_REG_M(REG_PMU_CLK_DIV3, 0xb, 0xff);
	//GMAC
	SET_REG_M(REG_PMU_CLK_DIV6, 0x1000000, 0xf000000);
	//UART0
	SET_REG_M(REG_PMU_CLK_DIV4, 0x5, 0x1f);
	//TIMER0
	SET_REG_M(REG_PMU_CLK_DIV5, 0x630000, 0xff0000);
	//PTS
	SET_REG_M(REG_PMU_CLK_DIV2, 0x63, 0x1ff);
	//WDT
	SET_REG_M(REG_PMU_CLK_DIV5, 0xc700, 0xff00);

#ifdef CONFIG_FH_MMC
#ifdef CONFIG_MMC_SDC1
	SET_REG_M(REG_PMU_CLK_GATE, 0x0, 0x400);
	SET_REG_M(REG_PMU_CLK_GATE1, 0x0, 0x8);
	/* Set Clock */
	SET_REG_M(REG_PMU_CLK_DIV3, 0x2000000, 0x0f000000);
#else
	SET_REG_M(REG_PMU_CLK_GATE, 0x0, 0x200);
	SET_REG_M(REG_PMU_CLK_GATE1, 0x0, 0x4);
#endif
#endif

#ifdef CONFIG_FH_GMAC
	SET_REG_M(REG_PMU_CLK_GATE, 0x0, 0x12000000);
#endif

#ifdef CONFIG_FH_SERIAL
	SET_REG_M(REG_PMU_CLK_GATE, 0x0, 0x2000);
#endif
#endif
	SCU_LOCK;

}
#ifdef CONFIG_BOARD_EARLY_INIT_F
int board_early_init_f(void)
{
	clock_init();

	return 0;
}
#endif

int board_init(void)
{
	/* Enable Ctrlc */
	/*console_init_f();*/
	gd->bd->bi_arch_number = MACH_TYPE_FH8862;
	/* adress of boot parameters */
	gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;
	fh_pinctrl_init(PIN_REG_BASE);
	
	/*hw_board_wdt_init();*/
#ifdef CONFIG_USB
	fh_usb_power_enable();
#endif
#if defined CONFIG_FH_DMA || defined CONFIG_FH_AXI_DMA
	dma_init();
#endif
#if defined CONFIG_FH_AES && defined CONFIG_FH_ALGORITHM_CORE
    fh_aes_probe();
#endif
#ifdef CONFIG_FH_SPI
extern struct spi_slave *fh_spi_setup_slave(unsigned int bus, unsigned int cs,
				unsigned int max_hz, unsigned int mode);
	spi_init();
	spi_bus_register(0, fh_spi_setup_slave);
	spi_bus_register(1, fh_spi_setup_slave);
	spi_bus_register(2, fh_spi_setup_slave);
#endif
	return 0;
}

int dram_init(void)
{
	gd->ram_size = PHYS_SDRAM_SIZE;
	return 0;
}

void dram_init_banksize(void)
{
	gd->bd->bi_dram[0].start = PHYS_SDRAM;
	gd->bd->bi_dram[0].size = PHYS_SDRAM_SIZE;
}


void reset_phy(void)
{

}

struct gmac_plat_info gmac_plat_data = {
	.regs = GMAC_REG_BASE,
	.id = 0,
	.phy_driver_list[0] = "Generic PHY",
	.phy_driver_list[1] = 0,
	.p_cfg_array = phy_config_array,
};

void *get_platform_eth_info(u32 id){
	return &gmac_plat_data;
}

int phy_inf_set(struct phy_device *phydev, int inf){

	SCU_UNLOCK;
	if(inf == PHY_INTERFACE_MODE_RMII){
		/*bit 100 : rmii; bit 000 : mii*/
		SET_REG_M(REG_PMU_GMAC_REG, 4, 7);
	}else if (inf == PHY_INTERFACE_MODE_RGMII){
		SET_REG_M(REG_PMU_GMAC_REG, 1, 7);
	}
	else{
		SET_REG_M(REG_PMU_GMAC_REG, 0, 7);
	}

	SCU_LOCK;
}

static int phy_sel_func(struct phy_device *phydev, int phy_sel)
{

	SCU_UNLOCK;

	if(phy_sel == EXTERNAL_PHY){
		printf("BETA :: phy sel external phy with 25M refclk\n");

		//close internal phy
		SET_REG_M(REG_PMU_EPHY_SEL, 0 << INSIDE_PHY_ENABLE_BIT_POS, 1 << INSIDE_PHY_ENABLE_BIT_POS);
		//ephy_clk 			[gate]
		SET_REG_M(REG_PMU_CLK_GATE1, 1 << 3, 1 << 3);
		//eth_ref_clk_out_g 		[gate]!!!!
		SET_REG_M(REG_PMU_CLK_GATE1, 1 << 0, 1 << 0);
		//eth_ref_clk_i div to 		[50M]
		//SET_REG_M(REG_PMU_CLK_DIV4, 1 << 28, 0xf << 28);
		// //eth_ref_clk_i div to 		[25M]
		SET_REG_M(REG_PMU_CLK_DIV4, 3 << 28, 0xf << 28);
		//eth_rmii_clk_g 			[ungate]
		SET_REG_M(REG_PMU_CLK_GATE1, 0 << 2, 1 << 2);
		//rmii_clk_mode switch		[1]
		SET_REG_M(REG_PMU_CLK_SEL2, 1 << 14, 1 << 14);
		//mac speed set 			[50M]
		SET_REG_M(REG_PMU_CLK_SEL2, 1 << 12, 3 << 12);
		//eth_clk_sel				[1]
		SET_REG_M(REG_PMU_CLK_SEL2, 1 << 15, 1 << 15);
		//rgmii_rmii_sel			[0]
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 16, 1 << 16);
		//rgmii_rx_clk_sel			[0]
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 17, 1 << 17);
		//eth_ref_clk_out_g 		[ungate]!!!!
		SET_REG_M(REG_PMU_CLK_GATE1, 0 << 0, 1 << 0);

	}
	else{
		printf("BETA :: phy sel internal phy\n");
		//open internal phy
		SET_REG_M(REG_PMU_EPHY_SEL, 1 << INSIDE_PHY_ENABLE_BIT_POS, 1 << INSIDE_PHY_ENABLE_BIT_POS);
		//ephy_clk 					[gate]
		SET_REG_M(REG_PMU_CLK_GATE1, 1 << 3, 1 << 3);
		//eth_ref_clk_out_g 		[gate]!!!!
		SET_REG_M(REG_PMU_CLK_GATE1, 1 << 0, 1 << 0);
		//eth_rmii_clk_g 			[gate]
		SET_REG_M(REG_PMU_CLK_GATE1, 1 << 2, 1 << 2);
		//eth_clk_sel				[0]
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 15, 1 << 15);
		//rgmii_rmii_sel			[0]
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 16, 1 << 16);
		//rgmii_rx_clk_sel			[0]
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 17, 1 << 17);
		//ephy_clk 					[ungate]
		SET_REG_M(REG_PMU_CLK_GATE1, 0 << 3, 1 << 3);
		//eth_ref_clk_out_g 		[ungate]!!!!
		SET_REG_M(REG_PMU_CLK_GATE1, 0 << 0, 1 << 0);

	}

	SCU_LOCK;

	return 0;
}

/*
-------phy in rmii mode-----
*** 50M -> phy refclk***
*** [phy 100M] :: 25M -> mac**
*** [phy 10M]  :: 2.5M -> mac**

-------phy in rgmii mode-----
*** 25M -> phy refclk***
*** [phy 1000M] :: 125M -> mac & mac in rgmii**
*** [phy 100M]  :: 25M -> mac & mac in mii**
*** [phy 10M]  :: 2.5M -> mac & mac in mii**
*/
static int gmac_set_spd(int speed, void *pri){
	struct phy_reg_cfg *reg_cfg = (struct phy_reg_cfg *)pri;
	//must be external phy spd set...
	SCU_UNLOCK;
	if(reg_cfg->inf_sup == PHY_INTERFACE_MODE_RGMII){
		//eth_ref_clk_i 		[25M]
		SET_REG_M(REG_PMU_CLK_DIV4, 3 << 28, 0xf << 28);
		//rgmii_tx_clk_gate		[ungate]
		SET_REG_M(REG_PMU_CLK_GATE1, 0 << 1, 1 << 1);
		//rgmii_tx_clk_polarity
		//SET_REG_M(REG_PMU_CLK_SEL2, 0 << 23, 1 << 23);
		//rgmii_tx_clk_dll
		//SET_REG_M(REG_PMU_DLL, 0 << 7, 0x3f << 7);
		//eth_clk_sel			[0]
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 15, 1 << 15);
		//rgmii_rmii_sel		[1]
		SET_REG_M(REG_PMU_CLK_SEL2, 1 << 16, 1 << 16);
		//rgmii_rx_clk_polarity
		//SET_REG_M(REG_PMU_CLK_SEL2, 0 << 24, 1 << 24);
		//rgmii_rx_clk_dll
		//SET_REG_M(REG_PMU_DLL, 0, 0x3f);

		//rgmii_rx_clk_sel		[0]
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 17, 1 << 17);
	}
	else{
		//eth_ref_clk_i 		[50M]
		SET_REG_M(REG_PMU_CLK_DIV4, 1 << 28, 0xf << 28);
		//rgmii_tx_clk_gate		[gate]
		SET_REG_M(REG_PMU_CLK_GATE1, 1 << 1, 1 << 1);
		//eth_clk_sel			[1]
		SET_REG_M(REG_PMU_CLK_SEL2, 1 << 15, 1 << 15);
		//rgmii_rmii_sel		[0]
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 16, 1 << 16);
		//rgmii_rx_clk_sel		[0]
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 17, 1 << 17);
	}


	switch(speed){
		case GMAC_SPEED_1000M:
		//mac_sed				[125M]
		SET_REG_M(REG_PMU_CLK_SEL2, 2 << 12, 3 << 12);
		break;
		case GMAC_SPEED_100M:
		//mac_sed
		SET_REG_M(REG_PMU_CLK_SEL2, 1 << 12, 3 << 12);
		break;
		case GMAC_SPEED_10M:
		SET_REG_M(REG_PMU_CLK_SEL2, 0 << 12, 3 << 12);
		break; 
	}
	SCU_LOCK;
	return 0;
}

#define BITS_PER_LONG 32
#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))

static inline unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
{
	unsigned long idx;

	for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
		if (addr[idx])
			return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
	}

	return size;
}


unsigned long find_last_bit(const unsigned long *addr, unsigned long size)
{
	if (size) {
		unsigned long val = BITMAP_LAST_WORD_MASK(size);
		unsigned long idx = (size-1) / BITS_PER_LONG;

		do {
			val &= addr[idx];
			if (val)
				return idx * BITS_PER_LONG + __fls(val);

			val = ~0ul;
		} while (idx--);
	}
	return size;
}

enum {
	CP_VAL_0,
	CP_VAL_1,
	VAL_100M_0,
	VAL_100M_1,
	VAL_10M_0,
	VAL_10M_1,
	TRAIN_MAX_SIZE,
};

static unsigned int train_100_array[] = {
	31, 30, 29, 28,
	27, 26, 25, 24,
	23, 22, 21, 20,
	19, 18, 17, 16,
	0,  1,  2,  3,
	4,  5,  6,  7,
	8,  9,  10, 11,
	12, 13, 14, 15,
};

static unsigned int train_10_array[] = {
	31, 30, 29, 28,
	27, 26, 25, 24,
	23, 22, 21, 20,
	19, 18, 17, 16,
	0,  1,  2,  3,
	4,  5,  6,  7,
	8,  9,  10, 11,
	12, 13, 14, 15,
};

static unsigned int train_cp_array[] = {
	0, 1, 2, 3,
};
#define REFIX_CP_TRAIN_DATA_OFFSET	    (0)
#define REFIX_100M_TRAIN_DATA_OFFSET    (0)
#define REFIX_10M_TRAIN_DATA_OFFSET	    (0)

struct s_train_val train_array[TRAIN_MAX_SIZE] = {

		[CP_VAL_0].name = "cp_val_0",
		[CP_VAL_0].src_add = REG_PMU_EPHY_PARAM,
		[CP_VAL_0].src_mask = 0xc0,
		[CP_VAL_0].src_vaild_index = 15,
		[CP_VAL_0].dst_add = 0x18,
		[CP_VAL_0].dst_vaild_index = 22,
		[CP_VAL_0].bind_train_array = train_cp_array,
		[CP_VAL_0].bind_train_size = ARRAY_SIZE(train_cp_array),
		[CP_VAL_0].usr_train_offset =
		REFIX_CP_TRAIN_DATA_OFFSET,

		[CP_VAL_1].name = "cp_val_1",
		[CP_VAL_1].src_add = REG_PMU_EPHY_PARAM,
		[CP_VAL_1].src_mask = 0xc0,
		[CP_VAL_1].src_vaild_index = 15,
		[CP_VAL_1].dst_add = 0xb7c,
		[CP_VAL_1].dst_vaild_index = 6,
		[CP_VAL_1].bind_train_array = train_cp_array,
		[CP_VAL_1].bind_train_size = ARRAY_SIZE(train_cp_array),
		[CP_VAL_1].usr_train_offset =
		REFIX_CP_TRAIN_DATA_OFFSET,

		[VAL_100M_0].name = "val_100M_0",
		[VAL_100M_0].src_add = REG_PMU_EPHY_PARAM,
		[VAL_100M_0].src_mask = 0x1f,
		[VAL_100M_0].src_vaild_index = 5,
		[VAL_100M_0].dst_add = 0x24,
		[VAL_100M_0].dst_vaild_index = 8,
		[VAL_100M_0].bind_train_array = train_100_array,
		[VAL_100M_0].bind_train_size = ARRAY_SIZE(train_100_array),
		[VAL_100M_0].usr_train_offset =
		REFIX_100M_TRAIN_DATA_OFFSET,

		[VAL_100M_1].name = "val_100M_1",
		[VAL_100M_1].src_add = REG_PMU_EPHY_PARAM,
		[VAL_100M_1].src_mask = 0x1f,
		[VAL_100M_1].src_vaild_index = 5,
		[VAL_100M_1].dst_add = 0x1308,
		[VAL_100M_1].dst_vaild_index = 8,
		[VAL_100M_1].bind_train_array = train_100_array,
		[VAL_100M_1].bind_train_size = ARRAY_SIZE(train_100_array),
		[VAL_100M_1].usr_train_offset =
		REFIX_100M_TRAIN_DATA_OFFSET,

		[VAL_10M_0].name = "val_10M_0",
		[VAL_10M_0].src_add = REG_PMU_EPHY_PARAM,
		[VAL_10M_0].src_mask = 0x1f00,
		[VAL_10M_0].src_vaild_index = 13,
		[VAL_10M_0].dst_add = 0x24,
		[VAL_10M_0].dst_vaild_index = 16,
		[VAL_10M_0].bind_train_array = train_10_array,
		[VAL_10M_0].bind_train_size = ARRAY_SIZE(train_10_array),
		[VAL_10M_0].usr_train_offset =
		REFIX_10M_TRAIN_DATA_OFFSET,

		[VAL_10M_1].name = "val_10M_1",
		[VAL_10M_1].src_add = REG_PMU_EPHY_PARAM,
		[VAL_10M_1].src_mask = 0x1f00,
		[VAL_10M_1].src_vaild_index = 13,
		[VAL_10M_1].dst_add = 0x1308,
		[VAL_10M_1].dst_vaild_index = 0,
		[VAL_10M_1].bind_train_array = train_10_array,
		[VAL_10M_1].bind_train_size = ARRAY_SIZE(train_10_array),
		[VAL_10M_1].usr_train_offset =
		REFIX_10M_TRAIN_DATA_OFFSET,
};

void dump_phy_train_val(struct s_train_val *p_train)
{
	printf("------------GAP_LINE---------------\n");
	printf("name            : %s\n", p_train->name);
	printf("src_add         : %08x\n", p_train->src_add);
	printf("src_mask        : %08x\n", p_train->src_mask);
	printf("src_vaild_index : %08x\n", p_train->src_vaild_index);
	printf("dst_add         : %08x\n", p_train->dst_add);
	printf("dst_vaild_index : %08x\n", p_train->dst_vaild_index);
	printf("usr_train_offset: %d\n", p_train->usr_train_offset);
}

void parse_train_data(struct s_train_val *p_train)
{
	unsigned long test_data;
	int i;
	unsigned int temp_data;
	unsigned int vaild_bit_pos;
	int max_offset, low_offset;
	unsigned int *array;
	unsigned int size;

	array = p_train->bind_train_array;
	size = p_train->bind_train_size;
	test_data = p_train->src_mask;
	vaild_bit_pos = find_first_bit(&test_data, 32);

	if ((!array) ||
	((p_train->src_mask >> vaild_bit_pos) != (size - 1))) {
		printf("para error..\n");
		return;
	}

	temp_data = GET_REG(p_train->src_add);
	if (temp_data & (1 << p_train->src_vaild_index)) {
		/*got vaild val*/
		temp_data &= p_train->src_mask;
		temp_data >>= vaild_bit_pos;
		printf("[%s] :: train val %d\n", p_train->name, temp_data);
		/*find index*/
		for (i = 0; i < size; i++) {
			if (temp_data == array[i]) {
				max_offset = (size - 1) - i;
				low_offset = 0 - i;
				break;
			}
		}

		if ((p_train->usr_train_offset < low_offset) ||
		(p_train->usr_train_offset > max_offset)) {
			printf("offset [%d] should limit in [%d , %d]\n",
			p_train->usr_train_offset, low_offset, max_offset);
			return;
		}
		//fix array idx..
		i += p_train->usr_train_offset;
		temp_data = array[i];
		/*printf("fix idx :: [%d] = %d\n", i, temp_data);*/
	} else {
		temp_data = 0;
		printf("[%s] :: use default train data %d\n",
		p_train->name, temp_data);
	}
	temp_data &= (p_train->src_mask >> vaild_bit_pos);
	SET_REG_M(REG_EPHY_BASE + p_train->dst_add,
	temp_data << p_train->dst_vaild_index,
	(p_train->src_mask >> vaild_bit_pos) << p_train->dst_vaild_index);
}


static void fh_internal_phy_reset(void){

	int idx = 0;
	unsigned int chip_id;

	chip_id = GET_REG(REG_PMU_CHIP_INFO);
	SCU_UNLOCK;
	SET_REG_M(REG_EPHY_BASE + 0, 0, 0x3<<7);
	SET_REG(REG_EPHY_BASE + 0x28, 0x00247875);
	SET_REG(REG_EPHY_BASE + 0x1274, 0x3c3);

	SET_REG_M(REG_PMU_CLK_GATE1, 0, 1<<0); // internal ephy_clk gate open 0:open 1:closed
	SET_REG_M(REG_PMU_SWRSTN_NSR, 0, 1<<23);
	SET_REG_M(REG_PMU_SWRST_APB_CTRL, 0, 1<<27);
	udelay(200);
	SET_REG_M(REG_PMU_SWRST_APB_CTRL, 1<<27, 1<<27); // prstn EPHY RESET
	SET_REG_M(REG_PMU_SWRSTN_NSR, 1<<23, 1<<23);  // rstn EPHY RESET

	SET_REG_M(REG_EPHY_BASE + 0x1370, 0x0, 1<<3);
	SET_REG(REG_EPHY_BASE + 0x1274, 0x3c3);
	SET_REG(REG_EPHY_BASE + 0x0b6C, 0xf04);
	SET_REG(REG_EPHY_BASE + 0x1340, 0x0000);
	SET_REG(REG_EPHY_BASE + 0x0bA8, 0x20);
	SET_REG(REG_EPHY_BASE + 0x0018, 0x00c02800);
	SET_REG(REG_EPHY_BASE + 0x001c, 0x00810000);
	SET_REG(REG_EPHY_BASE + 0x0020, 0x0000340c);
	if(chip_id == 0x00400009){
		//xgmp
		SET_REG(REG_EPHY_BASE + 0x0024, 0x30131500);
	}
	else{
		//xgm
		SET_REG(REG_EPHY_BASE + 0x0024, 0x10131500);
	}
	SET_REG(REG_EPHY_BASE + 0x0028, 0x01402800);
	SET_REG(REG_EPHY_BASE + 0x001c, 0xa1800000);
	SET_REG(REG_EPHY_BASE + 0x0028, 0x00402800);
	SET_REG(REG_EPHY_BASE + 0x0014, 0x06000000);
	SET_REG(REG_EPHY_BASE + 0x0020, 0x2340c);
	SET_REG(REG_EPHY_BASE + 0x000c, 0xba0edfff);
	SET_REG(REG_EPHY_BASE + 0x0010, 0x7fcbf9de);
	SET_REG(REG_EPHY_BASE + 0x0b98, 0xd150);
	SET_REG(REG_EPHY_BASE + 0x1224, 0);
	SET_REG(REG_EPHY_BASE + 0x12f0, 0);
	SET_REG(REG_EPHY_BASE + 0x0bc0, 0x7de);
	SET_REG(REG_EPHY_BASE + 0x0bc8, 0x09de);
	SET_REG(REG_EPHY_BASE + 0x070c, 0x0401);
	SET_REG(REG_EPHY_BASE + 0x0a84, 0x1d20);
	SET_REG(REG_EPHY_BASE + 0x06d4, 0x3130);
	SET_REG(REG_EPHY_BASE + 0x0a6c, 0x0818);
	SET_REG(REG_EPHY_BASE + 0x0aec, 0x1000);
	SET_REG(REG_EPHY_BASE + 0x0658, 0x1c00);
	SET_REG(REG_EPHY_BASE + 0x07d4, 0x6900);
	SET_REG(REG_EPHY_BASE + 0x06b8, 0x4);
	SET_REG(REG_EPHY_BASE + 0x06E4, 0x04a0);
	SET_REG(REG_EPHY_BASE + 0x0BBC, 0x1759);
	SET_REG(REG_EPHY_BASE + 0x0bf8, 0x0100);
	SET_REG(REG_EPHY_BASE + 0x0794, 0x9000);
	SET_REG(REG_EPHY_BASE + 0x0788, 0xf);
	if(chip_id == 0x00400009){
		//xgmp
		SET_REG(REG_EPHY_BASE + 0x1378, 0x1e26);
	}
	else{
		//xgm
		SET_REG(REG_EPHY_BASE + 0x1378, 0x1820);
	}
	SET_REG_M(REG_EPHY_BASE + 0x0, 0x20<<24, 0xff<<24);
	SET_REG_M(REG_EPHY_BASE + 0x4, 0x18, 0xff);
	SET_REG(REG_EPHY_BASE + 0x0680, 0x0552);
	SET_REG(REG_EPHY_BASE + 0x1274, 0);
	SET_REG(REG_EPHY_BASE + 0x0b6C, 0x704);
	SET_REG(REG_EPHY_BASE + 0x1340, 0);
	SET_REG(REG_EPHY_BASE + 0x0ba8, 0);
	SET_REG(REG_EPHY_BASE + 0x06c8, 0xc000);
	SET_REG(REG_EPHY_BASE + 0x06e0, 0x8508);
	SET_REG(REG_EPHY_BASE + 0x1328, 0x0065);
	SET_REG(REG_EPHY_BASE + 0x076c, 0x0430); //add link pulse thrh H
	SET_REG(REG_EPHY_BASE + 0x06b4, 0xfd00); //enable auto crossover in 100t and 10t
	SET_REG(REG_EPHY_BASE + 0x1360, 0x3f37);

	SET_REG_M(REG_EPHY_BASE + 0x0778, 0x24 << 8, 0xff << 8);
	SET_REG_M(REG_EPHY_BASE + 0x077c, 0x24, 0xff);

	SET_REG(REG_EPHY_BASE + 0xb10, 0x5000);
	SET_REG(REG_EPHY_BASE + 0xb14, 0x8000);
	SET_REG(REG_EPHY_BASE + 0xb1c, 0x000e);
	SET_REG(REG_EPHY_BASE + 0xb20, 0x000e);
	SET_REG(REG_EPHY_BASE + 0xb1c, 0x000f);
	SET_REG(REG_EPHY_BASE + 0xb20, 0x0035);
	SET_REG(REG_EPHY_BASE + 0xb1c, 0x0000);
	SET_REG(REG_EPHY_BASE + 0xb10, 0x0000);
	SET_REG(REG_EPHY_BASE + 0xb14, 0x0000);

	//led
	SET_REG(REG_EPHY_BASE + 0x2c, 0x13);
	SET_REG_M(REG_EPHY_BASE + 0x07f8, 3 << 8, 3 << 8);

	for (idx = 0; idx < ARRAY_SIZE(train_array); idx++) {
		/*dump_phy_train_val(&train_array[idx]);*/
		parse_train_data(&train_array[idx]);
	}
	SCU_LOCK;
}


void fh_internal_phy_link_up_cb(void)
{
	SET_REG(REG_EPHY_BASE + 0x0b10, 0x5000);
	SET_REG(REG_EPHY_BASE + 0x0b14, 0x8000);
	SET_REG(REG_EPHY_BASE + 0x0b1c, 0x000e);
	SET_REG(REG_EPHY_BASE + 0x0b20, 0x0004);
	SET_REG(REG_EPHY_BASE + 0x0aec, 0x1800);
	SET_REG(REG_EPHY_BASE + 0x0b10, 0x0000);
	SET_REG(REG_EPHY_BASE + 0x0b14, 0x0000);
}


void fh_internal_phy_link_down_cb(void)
{
	SET_REG(REG_EPHY_BASE + 0xb10, 0x5000);
	SET_REG(REG_EPHY_BASE + 0xb14, 0x8000);
	SET_REG(REG_EPHY_BASE + 0xb1c, 0x000e);
	SET_REG(REG_EPHY_BASE + 0xb20, 0x000e);
	SET_REG(REG_EPHY_BASE + 0xb1c, 0x000f);
	SET_REG(REG_EPHY_BASE + 0xb20, 0x0035);
	SET_REG(REG_EPHY_BASE + 0xb1c, 0x0000);
	SET_REG(REG_EPHY_BASE + 0xb10, 0x0000);
	SET_REG(REG_EPHY_BASE + 0xb14, 0x0000);
}

int board_eth_init(bd_t *bis)
{
	int rc = 0;

	struct phy_driver board_driver = {0};

	board_driver.phy_info[0].phy_sel = EXTERNAL_PHY;
	board_driver.phy_info[0].phy_reset = fh_gmac_phy_reset;
	board_driver.phy_info[0].ex_sync_mac_spd = gmac_set_spd;
	board_driver.phy_info[0].phy_inf = PHY_INTERFACE_MODE_RGMII;
	board_driver.phy_info[0].brd_link_up_cb = fh_external_phy_link_up_cb,
	board_driver.phy_info[0].brd_link_down_cb = fh_external_phy_link_down_cb,
	board_driver.phy_info[0].dll_set = fh_qos_set_phy_dll_val,
	board_driver.phy_info[0].polarity_set = fh_qos_set_phy_polarity,

	board_driver.phy_info[1].phy_sel = INTERNAL_PHY;
	board_driver.phy_info[1].phy_reset = fh_internal_phy_reset;
	board_driver.phy_info[1].ex_sync_mac_spd = 0;
	board_driver.phy_info[1].brd_link_up_cb = fh_internal_phy_link_up_cb,
	board_driver.phy_info[1].brd_link_down_cb = fh_internal_phy_link_down_cb,
	board_driver.phy_info[1].phy_inf = PHY_INTERFACE_MODE_MII;

	board_driver.inf_set = phy_inf_set;
	board_driver.phy_sel = phy_sel_func;
	board_driver.soc_para_set = soc_reg_update;


	//register phy driver
	genphy_init(&board_driver);

	rc = fh_qos_mac_initialize(bis);
	return rc;
}

void do_arc_jump(ulong addr)
{
	unsigned int arc_addr;

	arc_addr = ((addr & 0xffff) << 16) | (addr >> 16);

	// ungate arc clk
	SET_REG_M(REG_PMU_CPU_GATE, 0, BIT(1));

	printf("arc addr: 0x%x\n", arc_addr);

	// ARC Reset
	SCU_UNLOCK;
	SET_REG(REG_PMU_CPU_SWRST, 0xFFFFFFFD);


	SET_REG(REG_PMU_A625BOOT0 , 0x7940266B);
	SET_REG(REG_PMU_A625BOOT1 , arc_addr);  // Configure ARC Bootcode start address
	SET_REG(REG_PMU_A625BOOT2 , 0x0F802020);
	SET_REG(REG_PMU_A625BOOT3 , arc_addr);

	// ARC reset released
	SET_REG( REG_PMU_CPU_SWRST, 0xFFFFFFFF);

	// wait ramloader done, about 1024 ARC CPU cycle
	udelay(100);

	// start ARC625
	SET_REG_M( REG_PMU_A625_START_CTRL, 0xFF, 0x10);

	SCU_LOCK;
}

int sf_fastbootcmd(ulong cmd)
{
	memcpy(&env_ptr->data[FH_BOOTCMD_OFFSET - 4], &cmd, 4);
	return 0;
}

#ifdef CONFIG_FH_MMC
static int fh_mci_reset(unsigned int slot_id)
{
#if 0
	u32 reg;

	reg = 0xffffffff & (~BIT(slot_id ? 2 : 1));
	SCU_UNLOCK;

	SET_REG(REG_PMU_SWRST_AHB_CTRL, reg);
	while (GET_REG(REG_PMU_SWRST_AHB_CTRL) != 0xffffffff)
		;

	SCU_LOCK;
#endif
	return 0;
}

void fh_mci_sys_init(unsigned int slot_id)
{
	#define SMP_SHIFT           0

	unsigned int drv_shift;
	unsigned int sam_shift;
	unsigned int reg;

	SCU_UNLOCK;
	if (slot_id == 1) {
		drv_shift = 24;
		sam_shift = 28;
		fh_mci_reset(slot_id);

		/*//adjust clock phase...*/
		reg = GET_REG(REG_PMU_CLK_SEL1);

		reg &= ~(0x7 << drv_shift);
		reg &= ~(0x7 << sam_shift);
		reg |= (4 << drv_shift); /*//now drv fixed to 180.*/
		reg |= (SMP_SHIFT << sam_shift);

		SET_REG(REG_PMU_CLK_SEL1, reg);
	} else {
		drv_shift = 16;
		sam_shift = 20;

		fh_mci_reset(slot_id);

		/*//adjust clock phase...*/
		reg = GET_REG(REG_PMU_CLK_SEL1);

		reg &= ~(0x7 << drv_shift);
		reg &= ~(0x7 << sam_shift);
		reg |= (4 << drv_shift); /*//now drv fixed to 180.*/
		reg |= (SMP_SHIFT << sam_shift);

		SET_REG(REG_PMU_CLK_SEL1, reg);

	}
	SCU_LOCK;
}
#endif

#ifdef CONFIG_FH_GMAC
void GMAC_EarlyInit(void *gmac)
{

}
#endif

void reset_cpu (ulong addr)
{
	SCU_UNLOCK;

	SET_REG(REG_PMU_SWRST_MAIN_CTRL0, 0x7fffffff);
	
	SCU_LOCK;

	while (1)
		;
}

#ifdef CONFIG_GENERIC_MMC
extern int fh_mmc_init(int index);

int board_mmc_init(bd_t *bis)
{
    int ret = 0;
#ifdef FH_MMC

#ifdef FH_MMC_SDC0
	ret = fh_mmc_init(0);
	if (ret)
		printf("No SD0 device found !\n");
#endif

#ifdef FH_MMC_SDC1
    ret = fh_mmc_init(1);
    if (ret)
        printf("No SD1 device found !\n");
#endif

#endif
    return ret;
}
#endif


void fh_set_dma_hashdmasel(int en)
{

    if (en) {
        SET_REG_M(REG_PMU_DMA_HDSHAKE_EN, 1<<16, 1<<16);
    } else {
        SET_REG_M(REG_PMU_DMA_HDSHAKE_EN, 0<<16, 1<<16);
    }
}
